const path = require("path");
const fs = require("fs");
const globby = require("globby");
const merge = require("lodash.merge");
const fileExists = require('file-exists');
const nodeExternals = require('webpack-node-externals');
const webpack = require('webpack');
const CheckerPlugin = require('awesome-typescript-loader').CheckerPlugin;
const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ExtractTextPlugin = require('extract-text-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin');
const DefinePlugin = require('webpack/lib/DefinePlugin');
const rimraf = require("rimraf");
const mkdirp = require("mkdirp");
const gulp = require("gulp");
const webpackDevServer = require("webpack-dev-server");
const ts = require('gulp-typescript');
const babel = require('gulp-babel');
module.exports = {
    isObjectStr: function(str) {
        try {
            let res = JSON.parse(str);
            return res;
        } catch (e) {
            return false;
        }
    },
    copy: function() {
        let me = this;
        const obj = {};
        return globby([path.join(__dirname, 'copy/**'), path.join(__dirname, 'copy/.**'), "!" + path.join(__dirname, 'copy')])
            .then((paths) => {
                paths.forEach((filepath, index) => {
                    let stat = fs.statSync(filepath);
                    if (stat.isFile()) {
                        let extname = path.extname(filepath);
                        let targetpath = path.join(process.cwd(), path.basename(filepath));
                        let content = (extname == ".js" || extname == ".json") ? require(filepath) : fs.readFileSync(filepath).toString();
                        let iftargetExists = fileExists.sync(targetpath);
                        let targetContent = (extname == ".js" || extname == ".json") ? (iftargetExists ? require(targetpath) : {}) : (iftargetExists ? fs.readFileSync(targetpath).toString() : "");
                        let contentObj = (extname == ".js" || extname == ".json") ? content : me.isObjectStr(content);
                        let targetObj = (extname == ".js" || extname == ".json") ? targetContent : me.isObjectStr(targetContent);
                        if ((!!contentObj && !!targetObj) || (!iftargetExists && !!contentObj)) {
                            let _contnet = iftargetExists ? merge(targetObj, contentObj) : contentObj;
                            obj[targetpath] = {
                                value: _contnet,
                                isObj: true,
                                isJs: extname == ".js"
                            };
                        } else {
                            obj[targetpath] = {
                                value: content,
                                isObj: false,
                                isJs: extname == ".js"
                            };
                        }
                    }
                });
                return obj;
            });
    },
    init() {
        let me = this;
        const path = require("path");
        return me.copy().then((obj) => {
            // 根据配置建立目录
            return obj;
        })
    },
    // 构建前端代码
    buildFront(programpath, useserver, port, host) {
        let me = this;
        if (!programpath) {
            console.log("没有指定模块");
            return;
        } else {
            let result = me.analyseProgramPath(programpath, 1);
            let webpackConfig = me.initFrontConfig(result);
            if (useserver || webpackConfig.hasOwnProperty("devServer")) {
                let server = webpackConfig.devServer || {};
                let serverConfig = merge({
                    hot: true,
                    inline: true,
                    host: host || "localhost",
                    port: port || 8899,
                    stats: { colors: true }
                }, server);
                for (let entry in webpackConfig.entry) {
                    webpackConfig.entry[entry] = [webpackConfig.entry[entry]];
                    webpackConfig.entry[entry].unshift(`webpack-dev-server/client?http://${serverConfig.host}:${serverConfig.port}`, "webpack/hot/dev-server")
                };
                webpackConfig.plugins.push(new webpack.HotModuleReplacementPlugin())
                let devServer = new webpackDevServer(webpack(webpackConfig), serverConfig);
                devServer.listen(serverConfig.port, serverConfig.host, function() {
                    console.log(`服务器已启动: http://${serverConfig.host}:${serverConfig.port}`)
                })
            } else {
                // 首先清空目标
                rimraf.sync(path.join(result.dest));
                webpack(webpackConfig, function(err, stat) {
                    // rimraf(path.join(result.entry, "tsconfig.json"), () => {})
                    if (err) console.log(err);
                    console.log(stat.toString({
                        chunks: true,
                        colors: true
                    }))
                })
            };
        }
    },
    // 构建后端代码
    buildEnd(programpath) {
        let me = this;
        if (!programpath) {
            console.log("没有指定模块");
            return;
        } else {
            let result = me.analyseProgramPath(programpath, 0);
            me.compileEnd(result.entry, result.dest);
        }
    },
    // 0 end 1 front
    analyseProgramPath(programpath, type) {
        let rootConfigPath = path.join(process.cwd(), "fst.config.js")
        if (fileExists.sync(rootConfigPath)) {
            let rootConfig = require(rootConfigPath);
            let srcType = path.join(process.cwd(), path.join(rootConfig.src, type == 1 ? rootConfig.front : rootConfig.end));
            let destType = path.join(process.cwd(), path.join(rootConfig.dest, type == 1 ? rootConfig.front : rootConfig.end));
            let programEntry = path.join(srcType, programpath);
            let programDest = path.join(destType, programpath);
            let programConfigPath = path.join(programEntry, "fst.config.js");
            let programRootConfig = (rootConfig.hasOwnProperty("programs") && rootConfig.programs.hasOwnProperty(programpath)) ? (type == 1 ? rootConfig.programs[programpath].front : rootConfig.programs[programpath].end) : {};
            let programConfig = merge(programRootConfig, fileExists.sync(programConfigPath) ? require(programConfigPath) : {});
            return {
                entry: programEntry,
                dest: programDest,
                config: programConfig
            }
        } else {
            console.error(`${rootConfigPath}不存在`);
            return null;
        }
    },
    initFrontConfig(result) {
        let me = this;
        let IS_PROD = process.env.FST_ENV === "production";
        let webpackConfig = {
            entry: {},
            plugins: [],
            resolve: {
                extensions: ['.ts', ".js"]
            },
            output: {
                path: path.join(result.dest),
                filename: '[name][hash].js'
            },
            module: {
                rules: [{
                    test: /\.json$/,
                    use: 'json-loader'
                }, {
                    test: /\.html$/,
                    use: 'raw-loader',
                    // exclude: [helpers.root('src/index.html')]
                }, {
                    test: /\.(jpg|png|gif)$/,
                    use: 'file-loader'
                }, {
                    test: /\.(eot|woff2?|svg|ttf)([\?]?.*)$/,
                    use: 'file-loader'
                }]
            }
        };
        // 配置入口
        result.config.entry = result.config.hasOwnProperty("entry") ? result.config.entry : [{
            src: "polyfills.ts",
            name: "polyfills"
        }, {
            src: "main.ts",
            name: "main"
        }];
        let entry_quene = [];
        result.config.entry.forEach((entry, index) => {
            entry_quene.push(entry.name);
            const CommonsChunkPlugin = require('webpack/lib/optimize/CommonsChunkPlugin');
            webpackConfig.entry[entry.name] = path.join(result.entry, entry.src);
            webpackConfig.plugins.push(new CommonsChunkPlugin({
                name: entry.name,
                chunks: [entry.name]
            }))
        });
        webpackConfig.plugins.push(new CommonsChunkPlugin({
            name: entry_quene.reverse()
        }));
        // 初始化tsconfig.js
        let rootTsconfigPath = path.join(process.cwd(), "tsconfig.json");
        let rootTsconfig = fileExists.sync(rootTsconfigPath) ? require(rootTsconfigPath) : {};
        if (!result.config.hasOwnProperty("tsconfig")) {
            result.config.tsconfig = {};
        }
        result.config.tsconfig = merge(rootTsconfig, result.config.tsconfig);
        // 是否使用babel
        result.config.useBabel = result.config.useBabel == undefined ? true : result.config.useBabel;
        let tempTsconfigPath = path.join(result.entry, "tsconfig.json");
        fs.writeFileSync(tempTsconfigPath, JSON.stringify(result.config.tsconfig), "utf8");
        webpackConfig.module.rules.push({
            test: /\.ts(x?)$/,
            loader: 'awesome-typescript-loader',
            query: {
                useBabel: result.config.useBabel,
                useCache: true,
                configFileName: "tsconfig.json"
            }
        });
        // 配置服务器
        result.config.server = merge({
            enable: false,
            config: {
                port: 8899,
                host: "localhost",
                inline: true,
                disableHostCheck: true,
                hot: true,
                historyApiFallback: true,
                watchOptions: {
                    aggregateTimeout: 300,
                    poll: 1000
                }
            }
        }, result.config.hasOwnProperty("server") ? result.config.server : {});
        if (result.config.server.enable) {
            webpackConfig.devServer = result.config.server.config;
        }
        // 配置node
        result.config.node = me.mergeProperty(result.config, "node", {
            global: true,
            crypto: 'empty',
            process: true,
            module: false,
            clearImmediate: false,
            setImmediate: false
        });
        webpackConfig.node = result.config.node;
        // 压缩的配置
        result.config.uglify = me.mergeProperty(result.config, "uglify", {
            enable: IS_PROD,
            compress: {
                warnings: false,
                drop_console: IS_PROD
            }
        });
        result.config.uglify.enable && webpackConfig.plugins.push(new webpack.optimize.UglifyJsPlugin(merge({
            compress: {
                warnings: false,
                drop_console: IS_PROD
            },
        }, result.config.uglify)));
        // 抽取css  extractcss
        result.config.extractcss = me.mergeProperty(result.config, "extractcss", {
            enable: IS_PROD
        });
        if (result.config.extractcss.enable) {
            webpackConfig.module.rules.push({
                test: /\.css$/,
                loader: ExtractTextPlugin.extract({
                    use: 'css-loader'
                })
            });
            webpackConfig.module.rules.push({
                test: /\.less$/,
                loader: ExtractTextPlugin.extract({
                    use: 'css-loader!sass-loader'
                })
            });
            webpackConfig.module.rules.push({
                test: /\.scss$/,
                loader: ExtractTextPlugin.extract({
                    use: 'css-loader!less-loader'
                })
            });
            webpackConfig.plugins.push(new ExtractTextPlugin('[name].[contenthash].css'));
        } else {
            webpackConfig.module.rules.push({
                test: /\.css$/,
                use: 'style-loader!css-loader'
            });
            webpackConfig.module.rules.push({
                test: /\.less$/,
                use: 'style-loader!css-loader!less-loader'
            });
            webpackConfig.module.rules.push({
                test: /\.scss$/,
                use: 'style-loader!css-loade!sass-loaderr'
            })
        }
        // 配置HTML
        // webpackonfig.html 
        result.config.html = me.mergeProperty(result.config, "html", {
            template: path.join(result.entry, "index.html"),
            // title: "",
            chunksSortMode: 'dependency',
            // metadata: METADATA,
            inject: 'head'
        })
        webpackConfig.plugins.push(new HtmlWebpackPlugin(result.config.html))
            // 配置script加载方法
        result.config.script = me.mergeProperty(result.config, "script", {
            defaultAttribute: 'defer'
        });
        webpackConfig.plugins.push(new ScriptExtHtmlWebpackPlugin(result.config.script))
            // 变量设置
        result.config.variable = me.mergeProperty(result.config, "variable", {});
        webpackConfig.plugins.push(new DefinePlugin(result.config.variable));
        // 手动扩展
        result.config.extend = result.config.extend || function(c) { return c; };
        webpackConfig = result.config.extend(webpackConfig);
        return webpackConfig;
    },
    initFrontModule(names) {
        let fstConfig = require(path.join(process.cwd(), "fst.config.js"));
        let src = path.join(process.cwd(), fstConfig.src);
        names.forEach((module, index) => {
            let modulepath = path.join(src, path.join("front", module));
            // fs.writeFileSync(path.join)
            mkdirp.sync(modulepath);
            fs.writeFileSync(path.join(modulepath, "main.ts"), '"use strict"', 'utf8');
            fs.writeFileSync(path.join(modulepath, "polyfills.ts"), '"use strict"', 'utf8');
            fs.writeFileSync(path.join(modulepath, "style.less"), 'body {}', 'utf8');
            let html = `<!DOCTYPE html><html><head></head><body>Hello World!</body></html>
            `
            fs.writeFileSync(path.join(modulepath, "index.html"), html, 'utf8');
            fs.writeFileSync(path.join(modulepath, "fst.config.js"), `module.exports = {}`, 'utf8');
            fs.writeFileSync(path.join(modulepath, "package.json"), '{}', 'utf8');
        })

    },
    initEndModule(names) {
        let fstConfig = require(path.join(process.cwd(), "fst.config.js"));
        let src = path.join(process.cwd(), fstConfig.src);
        names.forEach((module, index) => {
            let modulepath = path.join(src, path.join("end", module));
            // fs.writeFileSync(path.join)
            mkdirp.sync(modulepath);
            fs.writeFileSync(path.join(modulepath, "main.ts"), '"use strict"', 'utf8');
            fs.writeFileSync(path.join(modulepath, "fst.config.js"), `module.exports = {}`, 'utf8');
            fs.writeFileSync(path.join(modulepath, "package.json"), '{}', 'utf8');
        })
    },
    mergeProperty(obj, key, begin) {
        return merge(begin, obj.hasOwnProperty(key) ? obj[key] : {});
    },
    compileEnd(dest, output) {
        let _dest = path.join(dest, "**/**.ts");
        // const tsProject = ts.createProject('tsconfig.json');
        let rootTsconfig = require(path.join(process.cwd(), "tsconfig.json"));
        // let destFstConfig = require(path)
        let destFstConfigPath = path.join(dest, "fst.config.js");
        let destTsconfig = fileExists(destFstConfigPath) ? require(destFstConfigPath).tsconfig : {};
        let config = merge(rootTsconfig, destTsconfig);
        rimraf.sync(output);
        let stream = gulp.src([_dest, "!node_modules/**/**"]).pipe(ts(config.compilerOptions));
        stream
            .pipe(gulp.dest(output));
    }
}